/*Main entry point for game*/
#include "SimulationManager.h"
//#include "ParticleCollection.h"
#include "ParticleIntegrator.h"
#include "3DCamera.h"
#include "glm\glm.hpp"
#include "TwoRadGridder.h"
#include "Particle.h"
#include "Sprite.h"
#include <cstdio>
#include <cmath>
#include <ctime>
#include "Kernels.h"

#define WINDOW_W 600
#define WINDOW_H 600

glm::vec3 gravity(0.0, -9.8, 0.0);

Kernel kernel;
float P0MOD = 2.0;
#define GRID_EPSILON 0.000
#define GAS_CONSTANT_K 3.0f//water
#define VISCOSITY_CONSTANT 0.5f
#define GAMMA 7.0f
glm::vec3 partmin (3, 4, 0.0),
		partmax(6, 8, 0.0);
#define GRID_MIN_PT 0 //used for grid
#define GRID_MAX_PT 10
#define MAX_PT 10 //uSED FOR CAMERA AND BOUNDS
#define MIN_PT 0
#define PARTICLE_INTERVAL 0.1

#define COEFF_REST 0.0
float supportRad = 0.3f;
float kernelh = supportRad;//supportRad /1.0f;
float kernelhSq = kernelh*kernelh;
float supportRadSq = supportRad*supportRad;
unsigned int gridNum = ((MAX_PT - MIN_PT)/supportRad)/2;
float particleMass = 1;

#define TIME_STEP 0.003f
#define COL_DIST 0.01
float P0 = 2.447939;
typedef TwoRadGridder< ParticleS, 2> GridderT;
typedef GridderT::GridBlock<ParticleS> GridBlockT;
typedef GridBlockT::iterator<ParticleS> GridBlockIterT;
typedef std::vector<GridderT::GridItemT>::iterator GridIter;


TwoRadGridder< ParticleS, 2 > * gr;

std::vector< glm::vec3 > normField;
std::vector<float> pressure;
std::vector<float> density;
std::vector<glm::vec3> debugForce;

std::vector<GridBlockT> nbvec;

//A global simulation manager.
//This takes care of most boiler-plate code.
//If you only want to draw something, you only need to do three things:
//1) Create a display func with the function signerature
//	void funcname()
//2) In main, call 
//	sm.SetCallbackDisplay(funcname)
//  where funcname is the name of your draw function.
//3) In main, after the above function call, call
//	sm.Run();
//  This will start running the simulation.
// 
//  For a list of the callbacks the user can set, look at the "SetCallback..." functions 
//  in the SimulationManager header file.
//  Also, the GetCamera() method will get the camera and allow you to call methods which move
//	around the scene or rotate, etc.

struct Bounds {
	glm::vec3 min;
	glm::vec3 max;
};

struct Cube {
	Bounds bounds[3]; //axis x, axis y, axis z
};

SimulationManager sm(WINDOW_W, WINDOW_H);

ParticleCollection pc;

EulerIntegratorFunctor pi;
Sprite sprite;
Bounds bounds;
bool doDrawGrid = false;
bool doShowOutline = false;
bool doShowRad = false;

float MAX_PRES, MIN_PRES;

//Triangle strip
std::vector< glm::vec2 > circleVerts;

void DebugFunc() {
	for (ParticleIter it = pc.begin();
		it != pc.end();
		++it){
		it->f.y += 100.0f;
	}
}

void DrawVector( glm::vec3 & start, glm::vec3 & vec ){
	glBegin(GL_LINES);
	glVertex3fv(&start[0]);
	glVertex3f(start.x + vec.x, start.y + vec.y, start.z + vec.z);
	glEnd();
}



void DrawSolidCircle( const glm::vec3 & origin, float rad ) {
	glBegin(GL_TRIANGLE_FAN);
		glVertex3fv( &origin[0] );

		for (int i = 0; i < circleVerts.size(); ++i) {
			glVertex3f(circleVerts[i].x*rad + origin.x,
				circleVerts[i].y*rad + origin.y,
				origin.z);
		}

	glEnd();
}

void DrawHollowCircle( const glm::vec3 & origin, float rad ) {
	unsigned int s = circleVerts.size();
	glBegin(GL_LINES);
		for (int i = 0; i < circleVerts.size(); ++i) {
			glVertex3f(circleVerts[i].x*rad + origin.x,
				circleVerts[i].y*rad + origin.y,
				origin.z);
			glVertex3f(circleVerts[(i+1) %s ].x*rad + origin.x,
				circleVerts[(i+1)%s].y*rad + origin.y,
				origin.z);
		}

	glEnd();
}

void InitCircleVerts( unsigned int numVerts) {
	float fracPi = 1.0f/numVerts;
	float twopi = 2*PI;

	for (int i = 0; i < numVerts + 2; ++i) {
		float x = std::cos(i*fracPi * twopi);
		float y = std::sin(i*fracPi * twopi);
		circleVerts.push_back(glm::vec2(x, y));
	}
}

void ToggleDrawGrid( ) {
	doDrawGrid = !doDrawGrid;
}

void ToggleDrawOutlines() {
	doShowOutline = !doShowOutline;
}

void DrawSupport( const glm::vec3 & inPart ) {
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

	
}

void DrawForce(){
	if (debugForce.size() == 0){
		return;
	}

	glBegin(GL_LINES);
	unsigned int cntr = 0;

	for (ParticleIter it = pc.begin();
		it != pc.end(); ++it){
			DrawVector(it->pos, debugForce[cntr++]);	
	}

	glEnd();	
}

void DrawNormals() {
	if (normField.size() == 0) {
		return;
	}
	glBegin(GL_LINES);
	
	for (ParticleIter it = pc.begin();
		it != pc.end(); ++it){
			DrawVector(it->f, normField[it->ndx]);		
	}

	glEnd();
}


void DrawParticles() {

	glPointSize(5.0);
	glBegin(GL_POINTS);
	ParticleIter en = pc.end();
	for (ParticleIter it = pc.begin();
		it != pc.end();
		++it ) {
			unsigned int ndx = it->ndx;
			float r = pressure[ndx] > 0 ? std::fabs(pressure[ndx]/MAX_PRES) : 0.0;
			float b = pressure[ndx] < 0 ? std::fabs(pressure[ndx] / MIN_PRES) : 0.0;
			glColor3f( r,
				0.0,
				 b);
			glVertex3fv(&(it->pos[0]));
	}
	glEnd();
}

void Draw2DBox( const Bounds & inBounds) {
	glLineWidth(3.0);

	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glBegin(GL_QUADS);

	glVertex2f(inBounds.min[0], inBounds.min[1]);
	glVertex2f(inBounds.max[0], inBounds.min[1]);
	glVertex2f(inBounds.max[0], inBounds.max[1]);
	glVertex2f(inBounds.min[0], inBounds.max[1]);

	glEnd();

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	
}

void DrawBox( const Bounds & inBounds ) {
	glLineWidth(3.0);

	glBegin(GL_LINES);
	glVertex3f(inBounds.min[0], inBounds.min[1], inBounds.min[2]);
	glVertex3f(inBounds.max[0], inBounds.min[1], inBounds.min[2]);

	glVertex3f(inBounds.min[0], inBounds.max[1], inBounds.min[2]);
	glVertex3f(inBounds.max[0], inBounds.max[1], inBounds.min[2]);

	glVertex3f(inBounds.min[0], inBounds.max[1], inBounds.max[2]);
	glVertex3f(inBounds.max[0], inBounds.max[1], inBounds.max[2]);

	glVertex3f(inBounds.min[0], inBounds.min[1], inBounds.max[2]);
	glVertex3f(inBounds.max[0], inBounds.min[1], inBounds.max[2]);

	//
	glVertex3f(inBounds.min[0], inBounds.min[1], inBounds.min[2]);
	glVertex3f(inBounds.min[0], inBounds.max[1], inBounds.min[2]);

	glVertex3f(inBounds.max[0], inBounds.min[1], inBounds.min[2]);
	glVertex3f(inBounds.max[0], inBounds.max[1], inBounds.min[2]);

	glVertex3f(inBounds.max[0], inBounds.min[1], inBounds.max[2]);
	glVertex3f(inBounds.max[0], inBounds.max[1], inBounds.max[2]);

	glVertex3f(inBounds.min[0], inBounds.min[1], inBounds.max[2]);
	glVertex3f(inBounds.min[0], inBounds.max[1], inBounds.max[2]);
	glEnd();
}

void DrawSupportRad() {
	for (ParticleCollection::iterator it = pc.begin();
		it != pc.end(); ++it) {
		DrawHollowCircle(glm::vec3(it->pos.x, it->pos.y, -1), supportRad);
	}	
}

void DrawOutlines() {
	float rad = 0.2;
	float lineWid = 0.05f;

	glColor3f(1.0, 0.0, 0.0);
	//glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	for (ParticleCollection::iterator it = pc.begin();
		it != pc.end(); ++it) {
		DrawSolidCircle(glm::vec3(it->pos.x, it->pos.y, -1), rad);
	}	

	glColor3f(1.0,1.0,1.0);
	for (ParticleCollection::iterator it = pc.begin();
		it != pc.end(); ++it) {
		DrawSolidCircle(glm::vec3(it->pos.x, it->pos.y, 0), rad - lineWid);
	}
}

//Display
//This function will be called as fast as possible, unlike the Update func.
//Only GL display code should be put in this section.  The screen
//is automatically cleared to white in the InitFrames() func, and the 
//glfwSwapBuffers() is called by the default EndFrames() function, so you only
//have to worry about actual drawing here.
void Display() {

	glColor3f(0.0, 1.0, 0.0);
	Draw2DBox(bounds);

	glColor3f(1.0, 0.0, 0.0);
	DrawParticles();

	glColor3f(1.0,1.0,0.0);
	//DrawNormals();

	//DrawForce();

	glColor3f(0.0, 1.0, 0.0);
	if (doDrawGrid){ 
		gr->Draw();
	}
	if (doShowRad) {
		glColor3f(0.0, 0.0, 1.0);
		DrawSupportRad();
	}

	if (doShowOutline) {
		DrawOutlines();
	}

}

void ForceBounds (const Bounds & inBounds, ParticleIter & pi){
	for (int i = 0; i < 2; ++i){
		if (pi->pos[i] < inBounds.min[i]) {
			pi->vel[i] = 0;
			pi->pos[i] = inBounds.min[i];
			//pi.vel()[i] *= -1*COEFF_REST;
		}
		if (pi->pos[i] > inBounds.max[i]) {
			pi->vel[i] = 0;
			pi->pos[i] = inBounds.max[i];
			//pi.vel()[i] *= -1*COEFF_REST;
		}
	}
}

void EmitBlock (glm::vec3 & min, glm::vec3 & max, float diam){

	for (float x = min.x; x < max.x; x += diam){
		for (float y = min.y; y < max.y; y+= diam){ 
			ParticleS part;
			part.pos = glm::vec3(x, y, 0.0);
			part.vel = glm::vec3(0.0, 0.0, 0.0);
			part.mass = particleMass;
			part.ndx = pc.size();
			pc.push_back(part);
		}
	}

	density.resize(pc.size());
	pressure.resize(pc.size());
	normField.resize(pc.size());
	nbvec.resize(pc.size());
}


/*glm::vec3 SumFunction( ParticleIter & p,
						GridBlock & nh,
						*/


//XXX Must precompute pressure at each particle and densities
glm::vec3 ComputePressure( const glm::vec3 & pi, const glm::vec3 & pj, float rad) {
	//Compute kernel gradient.
	float r = glm::length(pi-pj);
	return  (pi - pj)*kernel.KernelSpikeyGrad( r );
}


//Particle iterator and
//density
float CalcDensity( const ParticleIter & inP,
					GridBlockT & nh)
{
	float d = 0.0f;
	//Loop through particles in block)
	for (GridBlockIterT it = nh.begin();
		it != nh.end();
		++it){
			glm::vec3 diff = inP->pos - it.GetData()->pos;
			d += it.GetData()->mass *  kernel.KernelPoly6(glm::dot(diff, diff));
	}

	return d;
}

glm::vec3 CalcNormalField( const ParticleIter & inP,
						GridBlockT &nh,
						const std::vector<float> & density){
	const glm::vec3 & inPos = inP->pos;
	glm::vec3 n;
	//Loop through cells
	for (GridBlockIterT it = nh.begin();
		it != nh.end();
		++it){
		
			float densRecpi = (1.0f/density[it.GetData()->ndx]);

			glm::vec3 diff = (inPos - it.GetData()->pos);
			glm::vec3 tmpNorm = it.GetData()->mass * densRecpi * diff * kernel.KernelPoly6Grad(glm::length(inPos - it.GetData()->pos));

			n += tmpNorm;
	}

	return n;	
}

	

glm::vec3 CalcForce( const ParticleIter & inP,
								GridBlockT & nh,
								std::vector< float > & density,
								std::vector<float> & pressure) {

	unsigned int ndx = inP->ndx;						
	const glm::vec3 & inPos = inP->pos;
	glm::vec3 force (0.0f, 0.0f, 0.0f);

	//Loop through cells
	for (GridBlockIterT gbIt = nh.begin(); 
		gbIt != nh.end();
		++gbIt){
			GridIter it = gbIt.GetData();

			unsigned int nNdx = it->ndx;
			glm::vec3 diffVel = (it->vel - inP->vel);
			glm::vec3 diffPos = ((inP->pos - it->pos));
			float len = glm::length(diffPos);
			glm::vec3 normDiffPos;
			if (len != 0){
				normDiffPos = diffPos/len;
			}
			//	glm::vec3 normDiffPos = glm::normalize(diffPos);
			//Viscosity
			float r = glm::length(diffPos);
			force = force + VISCOSITY_CONSTANT*(it->mass * (diffVel / density[nNdx])*kernel.KernelViscosityLaplac( r ) );
			//Pressure
			float weight = it->mass * (pressure[ndx] + pressure[nNdx]) / (2*(density[ndx]));
			glm::vec3 pressForce = -(weight * (normDiffPos * (kernel.KernelSpikeyGrad(r))));
			force = force + pressForce;
	}

	//for (int i = 0; i < nh.cells.size(); ++i){
	//	//Loop through Particles.
	//	for (int p = 0; p < nh.cells[i]->size(); ++p){

	//		
	//		ParticleIter & it = nh.cells[i]->at(p);

	//		unsigned int nNdx = it.GetNdx();
	//		glm::vec3 diffVel = ((it.cVel() - inP.cVel()) / density[ndx]);
	//		glm::vec3 diffPos = ((inP.cPos() - it.pos()));

	//		//Viscosity
	//		force = force + VISCOSITY_CONSTANT*(it.cMass() * (diffVel / density[nNdx])*KernelViscosityLaplac( diffVel, supportRad ));

	//		//Pressure
	//		float weight = it.mass() * (pressure[ndx] + pressure[nNdx]) / (2*(density[nNdx]));

	//		glm::vec3 pressForce = - ( weight * (diffPos * (KernelSpikeyGrad(diffPos, supportRad))));

	//		force = force + pressForce;

	//		//Surface tension
	//		

	//		/*if (it != inP)
	//		{
	//			float lenr = glm::length(diffPos);
	//			if (lenr < COL_DIST) {
	//			//Move away
	//				force = force - (2*supportRad - lenr) * (diffPos / lenr);
	//			}
	//		}*/
	//	}
	//}

	return force;
}

class Timer {
public:
	void Start() {
		start = clock();
	}

	void Stop() {
		stop = clock();
	}

	float GetVal () {
		return ((float)stop - (float)start) / (float) CLOCKS_PER_SEC;
	}

	clock_t GetTicks() {
		return stop - start;
	}
private:
	clock_t start;
	clock_t stop;
};


void QueryNeighbors(){

	//Copy particles to grid.
	gr->Clear();
	gr->InsertData(pc);

	for (int i = 0; i < pc.size(); ++i){
		nbvec[i] = gr->GetNeighborhoodBlock(pc[i]);
	}

}

void IntegrateParticles(float ts){

	QueryNeighbors();
//	t.Stop();
	//printf("Secs: %f\n", t.GetVal());

	//unsigned int cntr = 0;
	MAX_PRES = -10000;
	MIN_PRES = 10000;
	//Density field
	unsigned int cntr = 0;
	//t.Start();
	

	for (ParticleIter pit = pc.begin();
		pit != pc.end();
		++pit, ++cntr){
		//Get the neighborhood.
			
			//xxx
			//GridBlockT nh = gr->GetNeighborhoodBlock(*pit);
			GridBlockT & nh = nbvec[cntr];
			unsigned int ndx = pit->ndx;

			density[ndx] = CalcDensity(pit, nh);

			//GAMMA == 1.0 means we don't need a power function. it's a bit quicker.
			if (GAMMA == 1.0){
				pressure[ndx] = GAS_CONSTANT_K * ((density[ndx]/P0) - 1);//GAS_CONSTANT_K * (density[ndx] - P0);
			}
			else {
				pressure[ndx] = GAS_CONSTANT_K * (std::pow(density[ndx]/P0, GAMMA) - 1);//GAS_CONSTANT_K * (density[ndx] - P0);
			}
			
			if (pressure[ndx] > MAX_PRES) {
				MAX_PRES = pressure[ndx];
			}
			if (pressure[ndx] < MIN_PRES) {
				MIN_PRES = pressure[ndx];
			}

	}
//	t.Stop();
//	printf("Secs: %f\n", t.GetVal());
	/*for (ParticleIter pit = pc.begin();
		pit != pc.end();
		++pit){
			//Normal field

			GridBlockT nh = gr->GetNeighborhoodBlock(*pit);

			unsigned int ndx = pit->ndx;

			normField[ndx] = CalcNormalField(pit, nh, density);
	}*/
	debugForce.clear();
	cntr = 0;
	//t.Start();
	
	for (ParticleIter pit = pc.begin();
		pit != pc.end();
		++pit, ++cntr){
			//
			//Add forces.
			//
			
			//xxx
			//GridBlockT nh = gr->GetNeighborhoodBlock(*pit);
			GridBlockT & nh = nbvec[cntr];
			
			//3. Calculate force on particle from pressure
			glm::vec3 fp;

			fp = CalcForce(pit, nh, density, pressure); 
			

		//	printf("Force :%f %f %f\n", fp.x, fp.y, fp.z);
			pit->f += fp;

			//Gravity
			if (pit->pos.y > bounds.min.y){
				//printf("Mass: %f\n", pit->mass);
				pit->f.y += gravity.y*pit->mass;//density[pit->ndx];
				//pit->vel.y += gravity.y*ts;
			}

			//Perform timestepping.
			//float len = glm::length(pit->f);
			//debugForce.push_back(pit->f/len);

			pi.Integrate(pit, ts, density[pit->ndx]);

			//
			//Boundary conditions
			ForceBounds(bounds, pit);
	}

	//t.Stop();

	//printf("Sec: %f\n", t.GetVal());
	//	
	//for (int currC = 0;
	//		currC < cells.cells.size(); 
	//		++currC){
	//	
	//		if (cells.cells[currC]->size() == 0){
	//			continue;
	//		}

	//		//Loop through nb.cells[0] which is the first cell containing the particle we already got.
	//		//We do this since all particles in this cell will share the same neighborhood.
	//		for (int currP = 0; 
	//			currP < cells.cells[currC]->size();
	//			++currP){	
	//	
	//			ParticleIter it = cells.cells[currC]->at(currP);// nb.cells[0]->at(currP);
	//		
	//			//Neighborhood for this cell.  All particles in cell share the same neighborhood.
	//			GridBlock nb = g->GetNH(it.pos().x, 
	//				it.pos().y, supportRad); //Gets for the first particle.

	//			
	//			unsigned int ndx = it.GetNdx();

	//			density[ndx] = CalcDensity(it, nb);
	//			pressure[ndx] = GAS_CONSTANT_K * (density[ndx] - P0);

	//			if(pressure[ndx] > MAX_PRES) { 
	//				MAX_PRES = pressure[ndx];
	//			}
	//			if (pressure[ndx] < MIN_PRES) {
	//				MIN_PRES = pressure[ndx];
	//			}

	//			cntr++;
	//		}
	//}

	//Normal field
	//for (int currC = 0;
	//		currC < cells.cells.size(); 
	//		++currC){
	//	
	//		if (cells.cells[currC]->size() == 0){
	//			continue;
	//		}

	//		//Loop through nb.cells[0] which is the first cell containing the particle we already got.
	//		//We do this since all particles in this cell will share the same neighborhood.
	//		for (int currP = 0; 
	//			currP < cells.cells[currC]->size();
	//			++currP){	
	//	
	//			ParticleIter it = cells.cells[currC]->at(currP);// nb.cells[0]->at(currP);
	//		
	//			//Neighborhood for this cell.  All particles in cell share the same neighborhood.
	//			GridBlock nb = g->GetNH(it.pos().x, 
	//				it.pos().y, supportRad); //Gets for the first particle.


	//			unsigned int ndx = it.GetNdx();

	//			normField[ndx] = CalcNormalField(it, nb, density);
	//		}
	//}

	//
	//Main force calculations.
	//for (int currC = 0;
	//	currC < cells.cells.size(); 
	//	++currC){
	//	
	//	if (cells.cells[currC]->size() == 0){
	//		continue;
	//	}


	//	//Loop through nb.cells[0] which is the first cell containing the particle we already got.
	//	//We do this since all particles in this cell will share the same neighborhood.
	//	for (int currP = 0; 
	//		currP < cells.cells[currC]->size();
	//		++currP){
	//			ParticleIter it = cells.cells[currC]->at(currP);

	//			GridBlock nb = g->GetNH(it.pos().x, 
	//			it.pos().y, supportRad); //Gets for the first particle.


	//			//
	//			//Add forces.
	//			//

	//			//3. Calculate force on particle from pressure
	//			glm::vec3 fp = CalcForce(it, nb, density, pressure); 

	//			it.force() += fp;

	//			//Gravity
	//			if (it.pos().y > 0){
	//				it.force().y += gravity.y*density[it.GetNdx()];
	//			}

	//			//Perform timestepping.
	//			pi.Integrate(it, ts, density[it.GetNdx()]);

	//			//
	//			//Boundary conditions
	//			ForceBounds(bounds, it);
	//	}

	//}

}


void CalcConstPress() {
	float p0 = 0;

	for (ParticleIter pit = pc.begin();
		pit != pc.end();
		++pit){
		
			GridBlockT nh = gr->GetNeighborhoodBlock(*pit);

			unsigned int ndx = pit->ndx;

			float dens = CalcDensity(pit, nh);

			p0 += dens / pc.size();
	}


	printf("Average dnesity: %f\n", p0);

	P0 = p0*P0MOD;


	//P0 = 1000.0;
	printf("Emitted %d particles\n", pc.size());
}

void DoShowRad() {
	doShowRad = !doShowRad;
}

void DoEmit() {
	static bool hasCalcConst = false;

	EmitBlock(partmin, partmax, PARTICLE_INTERVAL);

	//Recalculate P0
	
	//Copy particles to grid.
	gr->Clear();
	gr->InsertData(pc);
	if (!hasCalcConst){
		CalcConstPress();
		hasCalcConst = true;
	}
	
}

//Update
//This function will be called every timestep (ts).  The timestep can be
//set via the SetTimeStep method in SimulationManager. Put all non-drawing code
//here.
void Update(float ts) {

	IntegrateParticles( ts );

	//Emit particles
	//Emit(ts);
	
	//Integrate particles.

}

void Pause() {
	sm.Pause();


}

void LoadResources() {
	SpriteResource res;
	std::string filename = "ball.tga";
	if (!res.Load(filename)) {
		printf("Could not load file %s\n", filename.c_str());	
	}

	sprite.SetResource(res);

	//Key callbacks
	sm.AddKeyCallback((int)'P', Pause);
	sm.AddKeyCallback((int)'G', ToggleDrawGrid);
	sm.AddKeyCallback('D', DebugFunc);
	sm.AddKeyCallback('O', ToggleDrawOutlines);
	sm.AddKeyCallback('A', DoEmit);
	sm.AddKeyCallback('R', DoShowRad);

}

void Init() {

	//Sets the kernel's support radius
	kernel.SetKernelH(supportRad);

	glm::vec2 minPt = glm::vec2(GRID_MIN_PT, GRID_MIN_PT);
	glm::vec2 maxPt = glm::vec2(GRID_MAX_PT, GRID_MAX_PT);

	gr = new TwoRadGridder<ParticleS, 2> ( &minPt[0], &maxPt[0], supportRad );

	sm.SetCallbackLoadResources(LoadResources);

	sm.SetCallbackDisplay(Display);
	sm.SetCallbackUpdate(Update);

	sm.GetCamera().SetOrthogonal( true );
	sm.GetCamera().SetOrthoBounds( MIN_PT-0.01, MAX_PT+0.01, MIN_PT-0.01, MAX_PT+0.01 );
	

	sm.GetCamera().SetPos( glm::vec3(0.0, 0.0, 1.0) );
	sm.GetCamera().SetTarget( glm::vec3(0.0, 0.0, 0.0) );
	sm.GetCamera().SetNear( 0.1f );
	sm.GetCamera().SetFar( 100.0f );

	sm.SetTimeStep(TIME_STEP);

	bounds.min = glm::vec3(MIN_PT, MIN_PT, 0);
	bounds.max = glm::vec3(MAX_PT, MAX_PT, 0);

	InitCircleVerts(20);

	glEnable(GL_DEPTH_TEST);
}

int main (int argc, char ** argv ){

	Init();

	//XXX
	//Can initialize the camera and simulator params (callbacks) here
	//...

	//XXX

	//Start simulation.
	sm.Run();
}